Android for the Enterprise

Blake Meike

Blake Meike Marakana.com

Blake Meike

Blake

Developer, Architect, Android Evangelist

blake.meike@gmail.com

twitter: @callmeike

blog: http://portabledroid.wordpress.com/

… and who are YOU?

I think you’ll get the most out of this if:

Enterprise Android: It’s time!

These are just a few of the hundreds of articles that turn up in response to a query for "enterprise mobile"

It’s making people nervous…

The usually cited risks are:

Nervous

An issue of style

As developers, I think we face one more issue:

Skateboarder
Scientist

Web service developers and mobile developers have different understandings of what the environment looks like.

   Things web service devs take for granted

   Things mobile devs take for granted

   Common ground

Actually, an Android application looks a lot like a web application:

The Android Way

I’d like to talk in some detail about a couple of practices that may seem wrong at first, to one camp or the other (possibly to both).

Embrace them, though, and you will create better, simpler, and more reliable Enterprise Applications

Embracing Cursors

When a Web Service Developer has to deal with a database, the tool of choice is very likely to be some kind of Object-Relational Mapping framework:

Android ORM frameworks do exist:

No ORM

They might even be a good idea, in some applications…

In Android, forget ORM: Just use Cursors

   The Case for Cursors

   Wrapping the Cursor

One possibility: a lightweight bean-like accessor.

Very simple!

   POJO-like access to a Cursor: Example

public class EACursorAccess extends CursorAccess {
    public EACursorAccess(Cursor c) {
        super(c,
            EAContract.Columns.ID,
            EAContract.Columns.COL1,
            // other columns...
            EAContract.Columns.COLN);
    }

    public int getId() {
        return getCursor().getInt(getColIdx(EAContract.Columns.ID));
    }

    public String getCol1() {
        return getCursor().getString(getColIdx(EAContract.Columns.COL1));
    }

    // Other getters...

    public String getColN() {
        return getCursor().getString(getColIdx(EAContract.Columns.COLN));
    }
}

   POJO-like access to a Cursor: Use

ValuesCursorAccess pojo = new ValuesCursorAccess(cursor);
while (cursor.moveToNext()) {
    // most pojo operations are safe here...
    processPOJO(pojo);
}

   POJO-like access to a Cursor: Base Class

public abstract class CursorAccess {
    private final Cursor cursor;
    private final Map<String, Integer> colMap;

    public CursorAccess(Cursor c, String... cols) {
        cursor = c;
        Map<String, Integer> m = new HashMap<String, Integer>();
        for (String col: cols) {
            int idx = c.getColumnIndex(col);
            if (0 <= idx) { m.put(col, Integer.valueOf(idx)); }
        }
        colMap = Collections.unmodifiableMap(m);
    }

    protected final Cursor getCursor() { return cursor; }

    protected final int getColIdx(String col) {
        Integer idx = colMap.get(col);
        if (null == idx) {
            throw new IllegalArgumentException(
                "Cursor does not contain column: " + col);
        }
        return idx.intValue();
    }
 }

   Embracing Cursors

Instead of abstracting cursors away in Android,
use them, pervasively, as a standard abstraction for data

blank
blank
blank
blank

Next up: Networks are data…

Embracing “Dobjanschi” Architecture

If you are embarking on a connected application, you absolutely should view Virgil Dobjanschi’s 2010 Google I/O talk:

Dobjanschi

http://www.youtube.com/watch?v=xHXn3Kg2IQE

The architecture he proposes probably seems excessively complex…

In Android, a network connection should be fronted by a ContentProvider

   “Dobjanschi” Architecture

Dobjanschi Architecture

The “Figure Eight”:

   Outbound processing

Dobjanschi Architecture

   Inbound processing

Dobjanschi Architecture

   The Case for “Dobjanschi” Architecture

   The Activity

Headache

Losing AsyncTasks is a major win. They are a constant headache!

Headache

   The ServiceHelper (call)

Although it is, architecturally, a separate component, it is really quite simple.

Dobjanschi Architecture

Possible implementations include:

Here’s a simple implementation using a PendingIntent:

public void doSomething(Activity ctxt, String arg1, String argn) {
    Intent intent = new Intent(ctxt, EnterpriseService.class);
    intent.putExtra(EnterpriseService.ARG1, arg1);
    // add other args...
    intent.putExtra(EnterpriseService.ARGN, argn);
    intent.putExtra(
        EnterpriseService.CALLBACK,
        ctxt.createPendingResult(
            REQ_ID,
            new Intent(), // empty default response
            PendingIntent.FLAG_ONE_SHOT));
    ctxt.startService(intent);
}

   The Service

protected void onHandleIntent(Intent intent) {
    Intent resp = new Intent();
    resp.putExtra(
        RESULT,
        doSomething(
            intent.getExtras().getString(ARG1),
            // other args...
            // intent.getParcelableExtra(...)
            intent.getExtras().getString(ARGN)));

    try {
        ((PendingIntent) intent.getParcelableExtra(CALLBACK))
            .send(this, Activity.RESULT_OK, resp);
    }
    catch (CanceledException e) { Log.w(TAG, "Cancelled!", e); }
}

   ServiceHelper (response)

Called from Activity.onActivityResult:

public boolean onDoSomethingResult(
    int reqCode,
    int resCode,
    Intent resp,
    MessageHandler hdlr)
{
    if (REQ_ID != reqCode) { return false; }

    if (Activity.RESULT_OK != resCode) { hdlr.onFail(resCode); }
    else {
        hdlr.onMessage(resp.getExtras().getString(EnterpriseService.RESULT));
    }

    return true;
}

   Processor

The Processor is probably the most complex Component. This is complexity that used to be distributed across your AsyncTasks.

Dobjanschi Architecture

   The RESTMethod

Dobjanschi Architecture
DefaultHttpClient Documentation
Apache HTTP client has fewer bugs in Android 2.2 (Froyo) and earlier releases. For Android 2.3 (Gingerbread) and later, HttpURLConnection is the best choice.

   Embracing “Dobjanschi” Architecture

AsyncTasks hide state transitions in ephemeral objects.
Just don’t use them for network transactions.

blank
blank
blank
blank

Next up: The right place to record state transitions

Embracing the Content Provider

When a Web Service Developer has to deal with a database they probably think DAO.

The Android Documentation even states that, “unless you intend to share your data, you may not need a ContentProvider”

DAO

In Android, a ContentProvider is the DAO. Just use it.

   The Case for Content Providers

I admit that this case is harder to make than the previous two.

   Using the QueryBuilder

The virtual tables that your Content Provider publishes should not be the real tables or real columns. The QueryBuilder supports this separation.

   ProjectionMap Example

Note that the EXTRA column is completely virtual…

private static final Map<String, String> COL_AS_MAP;
static {
        Map<String, String> m = new HashMap<String, String>();
        m.put(EAContract.Columns.ID,
                DbHelper.COL_ID + " AS " + EAContract.Columns.ID);
        m.put(EAContract.Columns.COL1,
                DbHelper.COL_A + " AS " + EAContract.Columns.COL1);
        m.put(EAContract.Columns.COLN,
                "CASE WHEN " + DbHelper.COL_Z
                        + " NOT NULL THEN " + DbHelper.COL_ID
                        + " ELSE NULL END AS " + EAContract.Columns.COLN);
        COL_AS_MAP = Collections.unmodifiableMap(m);
}

   ProjectionMap Example (cont…)

The Query method gets really (safe and) simple.

private Cursor doQuery(
        String[] proj,
        String sel,
        String[] selArgs,
        String ord,
        long pk)
{
        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
        qb.setStrict(true);

        qb.setProjectionMap(COL_AS_MAP);

        qb.setTables(DbHelper.TABLE);

        if (0 <= pk) { qb.appendWhere(PK_CONSTRAINT + pk); }

        return qb.query(
                helper.getWritableDatabase(),
                proj,
                sel,
                selArgs,
                null,
                null,
                ord);
}

   Embracing Content Providers

Android thinks of data in terms of Content Providers
If you think that way too, your life will be easier

Building Enterprise Apps for Android

What would I like you to take away from this?

Slides and code at:

https://github.com/bmeike/AnDevConIV.git

/

#